package com.jhs.kjs.model.daumMap.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jhs.kjs.dto.CarSensorDTO;
import com.jhs.kjs.model.daumMap.dto.DaumMapDTO;
import com.jhs.kjs.model.daumMap.frame.ILatLng;

public class DaumMapModel {
	
	private double developerZoomLevel = 2;
	private int developerDirectionLevel = 3 * 1; // note. 절대 값이
																// 45를 넘어가서는
																// 안된다. max 40이
																// 적당하다.
	private Map<Integer, Integer> distanceUnit = new HashMap<Integer, Integer>();
	private Map<Integer, Integer> markerUnit = new HashMap<Integer, Integer>();
	
	private static final Logger logger = LoggerFactory
			.getLogger(DaumMapModel.class);
	
	private DaumMapDTO dto;
	public DaumMapModel(DaumMapDTO dto) {
		this.dto = dto;
		distanceUnit.put(1, 20);
		distanceUnit.put(2, 30);
		distanceUnit.put(3, 50);
		distanceUnit.put(4, 100);
		distanceUnit.put(5, 250);
		distanceUnit.put(6, 500);
		distanceUnit.put(7, 1000);
		distanceUnit.put(8, 2000);
		distanceUnit.put(9, 4000);
		distanceUnit.put(10, 8000);
		distanceUnit.put(11, 16000);
		distanceUnit.put(12, 32000);
		distanceUnit.put(13, 64000);
		distanceUnit.put(14, 128000);
		
		
		markerUnit.put(1, 20);
		markerUnit.put(2, 30);
		markerUnit.put(3, 50);
		markerUnit.put(4, 100);
		markerUnit.put(5, (int) (250*1.2));
		markerUnit.put(6, (int) (500*1.5));
		markerUnit.put(7, (int) (1000*1.4));
		markerUnit.put(8, (int) (2000*1.4));
		markerUnit.put(9, (int) (4000*1.4));
		markerUnit.put(10, 8000);
		markerUnit.put(11, 16000);
		markerUnit.put(12, 32000);
		markerUnit.put(13, 64000);
		markerUnit.put(14, 128000);
	}

	
	
	public int getTimeUnitByDistance(double distance){
		
		int dist = (int) (distance / 10);
		if(dist<20){
			return 1;
		}
		if(dist<30){
			return 1;
		}
		if(dist<50){
			return 1;
		}
		if(dist<100){
			return 2;
		}
		if(dist<250){
			return 2*2;
		}
		if(dist<500){
			return 2*2;
		}
		if(dist<1000){
			return 2*2;
		}
		if(dist<2000){
			return 2*3;
		}
		if(dist<4000){
			return 2*3;
		}
		if(dist<8000){
			return 2*3;
		}
		if(dist<16000){
			return 2*4;
		}
		if(dist<32000){
			return 2*4;
		}
		if(dist<64000){
			return 2*4;
		}
		if(dist<128000){
			return 2*5;
		}
		
		return 2*5;
	}
	
	public int getZoomByDistance(double distance){
		for(int i=1;i<distanceUnit.size();i++){
			if(distanceUnit.get(i).doubleValue() > (distance/10)){
				return i;
			}
		}
		return -1;
	}
	
	private int getMarkerUnit(){
		return markerUnit.get(dto.getZoom());
	}
	
	private int getUnit() {
		return distanceUnit.get(dto.getZoom());
	}
	
	private double getZoomLevel(){
		double a = developerZoomLevel;
		
		return a;
	}
	
	private int getDirectionLevel(){
		int level = developerDirectionLevel;
		level*=dto.getZoom();
		return level>90?90:level;
	}
	
	/**
	 * 자동차들의 위치 정보를 가지고, 지도의 세팅 값을 리턴한다.
	 * @param carList
	 * @return
	 */
	public DaumMapDTO getMapSetting(List<? extends ILatLng> carList){
		DaumMapDTO dto = new DaumMapDTO();
		double center_lat=0, center_lng=0;
		double min_lat = 100, min_lng=1000, max_lat=0, max_lng = 0;
		for(ILatLng car : carList){
			logger.debug(car.toString());
			if(car.getLat()==null || car.getLat().equals(new Double(0))) continue;
			if(car.getLng()==null || car.getLng().equals(new Double(0))) continue;
			
			if(min_lat>car.getLat()){	// 최소 값 구하기
				min_lat = car.getLat();
			}
			if(min_lng>car.getLng()){	// 최소 값 구하기
				min_lng = car.getLng();
			}
			
			if(max_lat<car.getLat()){	// 최대 값 구하기
				max_lat=car.getLat();
			}
			if(max_lng<car.getLng()){	// 최대 값 구하기.
				max_lng=car.getLng();
			}
		}
		
		center_lat=min_lat+(max_lat-min_lat)/2;
		center_lng=min_lng+(max_lng-min_lng)/2;
		
		logger.debug("center_lat = "+center_lat+", center_lng = "+center_lng);
		logger.debug("min_lat = "+min_lat+", min_lng = "+min_lng);
		logger.debug("max_lat = "+max_lat+", max_lng = "+max_lng);
		
		
		
//		double distance = Calc
		Map<String, Double> southWest = new HashMap<String, Double>();
		Map<String, Double> northEast = new HashMap<String, Double>();
		
		southWest.put("lat", min_lat);
		southWest.put("lng", min_lng);
		
		northEast.put("lat", max_lat);
		northEast.put("lng", max_lng);
		
		dto.setSwLat(min_lat);
		dto.setSwLng(min_lng);
		dto.setNeLat(max_lat);
		dto.setNeLng(max_lng);
		
		dto.setSouthWest(southWest);
		dto.setNorthEast(northEast);
		dto.setLat(center_lat);	// 평균값... 즉 가운데.
		dto.setLng(center_lng);
		
		int zoom=10;
		
		int distance = (int) (Calc(max_lat, max_lng, min_lat, min_lng)/1000);
		if(distance>550){
			zoom = 14;
		}else if(distance > 360){
			zoom = 13;
		}else if(distance > 240){
			zoom = 12;
		}else if(distance > 120){
			zoom = 11;
		}
		
		dto.setZoom(zoom);
		return dto;
	}
	
/**
 * 차량 위치 정보를 압축한다.	
 * @param rawList
 * @return
 */
	public List<ILatLng> compression(List<ILatLng> rawList) {
		logger.debug("rawList size = "+rawList.size());
		ILatLng start = rawList.get(0);
		List<ILatLng> retList = new ArrayList<ILatLng>();
		retList.add(start);
		for (ILatLng latLng : rawList) {
			if (Calc(start, latLng) > getUnit() * getZoomLevel()) {
				retList.add(latLng);
				start = latLng;
			}
			if (changeDirection(start, latLng, getDirectionLevel())) {
				retList.add(latLng);
				start = latLng;
			}
		}
		
		retList.add(rawList.get(rawList.size()-1));
		
		logger.debug("retList size = "+retList.size());
		return retList;
	}
	
	
	public List<CarSensorDTO> compressionByZoom(List<? extends CarSensorDTO> rawList) {
		logger.debug("rawList size = "+rawList.size());
		if(rawList.size()==0 || rawList==null) return new ArrayList<CarSensorDTO>();
		
		CarSensorDTO start = rawList.get(0);
		List<CarSensorDTO> retList = new ArrayList<CarSensorDTO>();
		retList.add(start);
		logger.debug("getMarkerUnit() = "+getMarkerUnit()+", getZoomLevel() = "+getZoomLevel());
		
		int count = 0;
		for (CarSensorDTO latLng : rawList) {
//			if(!isLatLngInArea(latLng)) continue;
			
			
			if(getMarkerUnit()<51){
				if((Calc(start, latLng))>getMarkerUnit()){
					retList.add(latLng);
					start = latLng;	
				}
				logger.debug("(Calc(start, latLng) = "+(Calc(start, latLng)));	
				
			}else
			if(getMarkerUnit()<101){
				if((Calc(start, latLng))>getMarkerUnit()){
					retList.add(latLng);
					start = latLng;	
				}
			}else
			if (Calc(start, latLng) > getMarkerUnit() * getZoomLevel()) {
				retList.add(latLng);
				start = latLng;
			}
		}
		rawList.get(0).setDirection(-1);
		retList.add(rawList.get(rawList.size()-1));
		
		logger.debug("retList size = "+retList.size());
		return retList;
	}
	
	public ILatLng getMaxArea(){
		ILatLng lat = new DaumMapDTO();

		double latGap = (dto.getOriMaxLat() - dto.getOriMinLat())/2;
		double lngGap = (dto.getOriMaxLng() - dto.getOriMinLng())/2;
		
		double maxLat = dto.getOriMaxLat() + latGap;
		double maxLng = dto.getOriMaxLng() + lngGap;
		
		lat.setLat(maxLat);
		lat.setLng(maxLng);
		return lat;
	}
	
	
	public ILatLng getMinArea(){
		ILatLng lat = new DaumMapDTO();

		double latGap = (dto.getOriMaxLat() - dto.getOriMinLat())/2;
		double lngGap = (dto.getOriMaxLng() - dto.getOriMinLng())/2;
		
		double minLat = dto.getOriMinLat() - latGap;
		double minLng = dto.getOriMinLng() - lngGap;
		
		lat.setLat(minLat);
		lat.setLng(minLng);
		logger.debug("latGap ="+latGap);
		logger.debug("lngGap ="+lngGap);
		return lat;
	}
	
	
	/**
	 * 두배의 거리 내에서 찾는다.
	 * @param latLng
	 * @return
	 */
	private boolean isLatLngInArea(ILatLng latLng){
		
//		logger.debug(dto.toString());
		double latGap = (dto.getOriMaxLat() - dto.getOriMinLat())/2;
		double lngGap = (dto.getOriMaxLng() - dto.getOriMinLng())/2;
		
		double maxLat = dto.getOriMaxLat() + latGap;
		double maxLng = dto.getOriMaxLng() + lngGap;
		
		double minLat = dto.getOriMinLat() - latGap;
		double minLng = dto.getOriMinLng() - lngGap;
		
//		logger.debug("latLng.getLat() = "+latLng.getLat()+", maxLat = "+maxLat);
//		logger.debug("latLng.getLat() = "+latLng.getLat()+", minLat = "+minLat);
//		logger.debug("latLng.getLng() = "+latLng.getLng() +", maxLng = "+maxLng);
//		logger.debug("latLng.getLng() = "+latLng.getLng() +", minLng = "+minLng);
		if(latLng.getLat() < maxLat && latLng.getLat() > minLat){
			if(latLng.getLng() < maxLng && latLng.getLng() > minLng){
				return true;
			}
		}
		
		return false;
	}
	
	public boolean contain(ILatLng latLng) {
		return dto.contain(latLng);
	}
	
	private boolean changeDirection(ILatLng la1, ILatLng la2, int level) {
		boolean ret = false;
		int angle1 = la1.getDirection();
		int angle2 = la2.getDirection();
		int big = 0, small= 0;
		if (angle1 > angle2) {
			big = angle1;
			small = angle2;
		} else if (angle2 > angle1) {
			big = angle2;
			small = angle1;
		} else {
			ret =  false; // 같으면
		}

		if (big - small > 180) {
			if ((360 - big + small) > level) {
				ret =   true;
			} else {
				ret =   false;
			}
		} else {
			if ((big - small) > level) {
				ret =   true;
			} else {
				ret =   false;
			}
		}
		
		return ret;
	}
	/**
	 * 거리 구하기.
	 * @return
	 */
	public double getDistance(){
		return Calc(dto.getNeLat(), dto.getNeLng(), dto.getSwLat(), dto.getSwLng());
	}
	public static double Calc(ILatLng latLng1, ILatLng latLng2) {
		return Calc(latLng1.getLat(), latLng1.getLng(), latLng2.getLat(),
				latLng2.getLng());
	}

	public static double Calc(double lat1, double lng1, double lat2, double lng2) {
		double dDistance = 0;
		double dLat1lnRad = lat1 * (Math.PI / 180.0);
		double dLong1lnRad = lng1 * (Math.PI / 180.0);
		double dLat2lnRad = lat2 * (Math.PI / 180.0);
		double dLong2lnRad = lng2 * (Math.PI / 180.0);
		double dLongitude = dLong2lnRad - dLong1lnRad;
		double dLatitude = dLat2lnRad - dLat1lnRad;
		double a = Math.pow(Math.sin(dLatitude / 2.0), 2.0)
				+ Math.cos(dLat1lnRad) * Math.cos(dLat2lnRad)
				* Math.pow(Math.sin(dLongitude / 2.0), 2.0);
		double c = 2.0 * Math.atan2(Math.sqrt(a), Math.sqrt(1.0 - a));
		double kEarth = 6376.5;
		dDistance = kEarth * c;
		
		return dDistance * 1000;
	}

}


